/* 
 * Christian's Set Top Box (STB) firewire command sender
 *
 * This program will accept a command line paramater to send a specific
 * command to a specific set top box via firewire.
 * There is no verification of the device type and assumes that this was
 * predetermined say via a script such as stb_cmd_guid.
 *
 * USAGE: Usage: stb-command [-ehrtv] -n <node> <command>
 *          -e : extended length command (may be required for some STBs. Adds NULL operands
 *          -h : detailed Help including supported commands
 *          -n : Node for command - required
 *          -r : use key Release instead of key press
 *          -t : Test mode - does not actually send the message
 *          -v : Verbose Mode
 *   <command> : command to send to the set top box (use -h for list of commands
 *
 * RETURN Value: Dependent on command
 *               -1 = error
 *               0  = success but no return value
 *               power_on, power_off, and power_status return:
 *               1 = OFF
 *               2 = ON
 *
 * requires: libavc1394-dev libraw1394-dev
 *
 * compile with: gcc -o stb-command stb-command.c -lrom1394 -lavc1394 -lraw1394
 *
 * Copyright 2009 by Christian Szpilfogel <myfirstname@mylastname.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

 * 

*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libavc1394/rom1394.h>
#include <libavc1394/avc1394.h>
#include <libraw1394/raw1394.h>

#define CMD_SUCCESS	0
#define CMD_STATUS	0
#define CMD_OFF		1
#define CMD_ON		2

#define CMDSTB_MASK	    0x00FFFFFF
#define CMDSTB_ERROR	-1
#define CMDSTB_TEST	-2
/* old values: 
#define CMDSTB_STATUS   0x01FFb27F 
#define CMDSTB_OFF      0x00FFb260
#define CMDSTB_ON       0x00FFb270
*/

/* Predefine standard POWER related commands */
#define CMDSTB_CMD_OPERAND_POWER_STATUS	0x7F    /* not defined in avc1394.h */
#define CMDSTB_STATUS	AVC1394_CTYPE_STATUS | AVC1394_SUBUNIT_TYPE_UNIT | AVC1394_SUBUNIT_ID_IGNORE | \
			AVC1394_COMMAND_POWER | CMDSTB_CMD_OPERAND_POWER_STATUS
#define CMDSTB_OFF      AVC1394_CTYPE_CONTROL | AVC1394_SUBUNIT_TYPE_UNIT | AVC1394_SUBUNIT_ID_IGNORE | \
			AVC1394_COMMAND_POWER | AVC1394_CMD_OPERAND_POWER_OFF
#define CMDSTB_ON       AVC1394_CTYPE_CONTROL | AVC1394_SUBUNIT_TYPE_UNIT | AVC1394_SUBUNIT_ID_IGNORE | \
			AVC1394_COMMAND_POWER | AVC1394_CMD_OPERAND_POWER_ON

/* Predine standard PANEL Command header */
#define CMDSTB_PANEL	AVC1394_CTYPE_CONTROL | AVC1394_SUBUNIT_TYPE_PANEL | AVC1394_SUBUNIT_ID_0 | \
			AVC1394_PANEL_COMMAND_PASS_THROUGH
#define CMDSTB_PANEL_NO_PARAMETER	0x00000000

/* Define newer PANEL Operands not in avc1394.h */
#define CMDSTB_PANEL_OPERATION_PLAY_FUNCTION		0x60
#define CMDSTB_PANEL_OPERATION_PAUSE_PLAY_FUNCTION	0x61
#define CMDSTB_PANEL_OPERATION_RECORD_FUNCTION		0x62
#define CMDSTB_PANEL_OPERATION_PAUSE_RECORD_FUNCTION	0x63
#define CMDSTB_PANEL_OPERATION_STOP_FUNCTION		0x64
#define CMDSTB_PANEL_OPERATION_MUTE_FUNCTION		0x65
#define CMDSTB_PANEL_OPERATION_RESTORE_VOLUME_FUNCTION	0x66
#define CMDSTB_PANEL_OPERATION_TUNE_FUNCTION		0x67
#define CMDSTB_PANEL_OPERATION_SELECT_DISK_FUNCTION	0x68
#define CMDSTB_PANEL_OPERATION_SELECT_AV_INPUT_FUNCTION	0x69
#define CMDSTB_PANEL_OPERATION_SELECT_AUDIO_INPUT_FUNCTION 0x6A

int verbose = 0;
int test_mode = 0;

quadlet_t *avc_cmd(raw1394handle_t handle, int node, quadlet_t cmd[], int cmd_length);

void usage()
{
   fprintf(stderr, "Usage: stb-command [-ehrtv] -n <node> <command>\n");
   fprintf(stderr, "  -e : extended length command (may be required for some STBs). Adds NULL operands\n");
   fprintf(stderr, "       For basic panel commands normally one word command is required but the spec\n");
   fprintf(stderr, "       does not clearly state this. -e allows the 2nd word to be populated with no\n");
   fprintf(stderr, "       parameters for the command (ie. parameter length =0 and following data is 0)\n");
   fprintf(stderr, "       in case some STBs require this\n");
   fprintf(stderr, "  -h : detailed Help including supported commands\n");
   fprintf(stderr, "  -n : Node for command - required\n");
   fprintf(stderr, "  -r : use key Release instead of key press\n");
   fprintf(stderr, "  -t : Test mode - does not actually send the message\n");
   fprintf(stderr, "  -v : Verbose Mode\n");
   fprintf(stderr, "  <command> : command to send to the set top box (use -h for list of commands)\n");
}

void usage_help()
{
   usage();
   fprintf(stderr, "\n");
   fprintf(stderr, "This program supports Unit Power and Panel Key Passthrough Commands.\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "Commands can be upper or lower case  you can use _ and + for readability.\n");
   fprintf(stderr, "All _ and + characters will be ignored so  you can use these for readability.\n");
   fprintf(stderr, "  For example:\n");
   fprintf(stderr, "     Power_ON\n");
   fprintf(stderr, "     CONTENTS_menu\n");
   fprintf(stderr, "     LEFT+UP\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "Function commands (e.g. Tuner_function, play_function) are not yet supported.\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "RETURN Value: Dependent on command\n");
   fprintf(stderr, "              -1 = error\n");
   fprintf(stderr, "               0  = success but no return value\n");
   fprintf(stderr, "               power_on, power_off, and power_status return:\n");
   fprintf(stderr, "                  1 = OFF\n");
   fprintf(stderr, "                  2 = ON\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "The following commands are supported and the description comes from the spec:\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "Power Commands - Unit wide power commands\n");
   fprintf(stderr, "                 Return code indicates power state after all actions (1=OFF, 2=ON)\n");
   fprintf(stderr, " power_on          - Turn set top box on\n");
   fprintf(stderr, " power_off         - Turn set top box off\n");
   fprintf(stderr, " power_status      - Determine current power state\n");
   fprintf(stderr, "\n");
   fprintf(stderr, "Panel Passthrough Commands - commands meant to mimic a user's actions\n");
   fprintf(stderr, " select            - Selects the item focused by cursor.\n");
   fprintf(stderr, " up                - Moves cursor upper direction.\n");
   fprintf(stderr, " down              - Moves cursor lower direction.\n");
   fprintf(stderr, " left              - Moves cursor left direction.\n");
   fprintf(stderr, " right             - Moves cursor right direction.\n");
   fprintf(stderr, " right_up          - Moves cursor upper-right direction.\n");
   fprintf(stderr, " right_down        - Moves cursor lower-right direction.\n");
   fprintf(stderr, " left_up           - Moves cursor upper-left direction.\n");
   fprintf(stderr, " left_down         - Moves cursor lower-left direction.\n");
   fprintf(stderr, " root_menu         - Displays initial menu to start GUI operation. The menu displayed\n");
   fprintf(stderr, "                     with this command is target-dependent, so it might be contents\n");
   fprintf(stderr, "                     menu, setup menu, favorite menu or the other menu, furthermore\n");
   fprintf(stderr, "                     it may be changed dynamically according to the status of the\n");
   fprintf(stderr, "                     target. This command may be used to finish GUI operation alternately.\n");
   fprintf(stderr, " setup_menu        - Displays set up menu such as option set up. (Can be used as a\n");
   fprintf(stderr, "                     shortcut.) The menu displayed with this command should be\n");
   fprintf(stderr, "                     designed to be reached from the initial menu of the target.\n");
   fprintf(stderr, " contents_menu     - Displays contents menu. (Can be used as a shortcut.) \n");
   fprintf(stderr, "                     For example, this command may be used to display the Electric\n");
   fprintf(stderr, "                     Program Guide (EPG) or the contents list in a storage medium.\n");
   fprintf(stderr, "                     The menu displayed with this command should be designed to be\n");
   fprintf(stderr, "                     reached from the initial menu of the target.\n");
   fprintf(stderr, " favorite_menu     - Displays user preset menu. (Can be used as a shortcut.)\n");
   fprintf(stderr, "                     For example, this command may be used to display the list of\n");
   fprintf(stderr, "                     user preset channel. The menu displayed with this command\n");
   fprintf(stderr, "                     should be designed to be reached from the initial menu of the target.\n");
   fprintf(stderr, " exit              - Closes current menu and go back previous menu. This command may\n");
   fprintf(stderr, "                     also be used to finish GUI operation, but a target shall be\n");
   fprintf(stderr, "                     implemented to be finished GUI operation without this command.\n");
   fprintf(stderr, " 0 - 9             - Input a numerical value.\n");
   fprintf(stderr, " dot               - Used with 0-9 to input numerical value such as a sub channel in US.\n");
   fprintf(stderr, " enter             - Fix the entered numerical value. Target should be implemented\n");
   fprintf(stderr, "                     to fix the entered value in any way without this command,\n");
   fprintf(stderr, "                     such as time out.\n");
   fprintf(stderr, " clear             - Cancel the entered numerical value.\n");
   fprintf(stderr, " Channel_up        - Switches the channel, such as broadcast channel, to upper one,\n");
   fprintf(stderr, "                     i.e. plus direction in number.\n");
   fprintf(stderr, " Channel_down      - Switches the channel, such as broadcast channel, to lower one,\n");
   fprintf(stderr, "                     i.e. minus direction in number.\n");
   fprintf(stderr, " previous_channel  - Switches to the previously selected channel.\n");
   fprintf(stderr, "                     For example, in case 123 ch was switched to 246 ch, this command\n");
   fprintf(stderr, "                     can be used as a switcher between 123 ch and 246 ch.\n");
   fprintf(stderr, " sound_select      - Used to switch the sound such as multiple language selection.\n");
   fprintf(stderr, " input_select      - Used to switch the input signal.\n");
   fprintf(stderr, " display_info      - Displays the information about current contents.\n");
   fprintf(stderr, "                     For example, this command may be used to display the channel\n");
   fprintf(stderr, "                     number, broadcaster and broadcast time, or recorded date\n");
   fprintf(stderr, "                     and timecode.\n");
   fprintf(stderr, " help              - Displays help instructions.\n");
   fprintf(stderr, " page_up           - Scrolls up the whole screen or part of display.\n");
   fprintf(stderr, " page_down         - Scrolls down the whole screen or part of display.\n");
   fprintf(stderr, " power_button      - Controls the power state of the device alternatively. This\n");
   fprintf(stderr, "                     command may support to turn the device off only.\n");
   fprintf(stderr, " volume_up         - Turns the volume to high.\n");
   fprintf(stderr, " volume_down       - Turns the volume to low.\n");
   fprintf(stderr, " mute              - Puts the sound out, and may resume it alternatively or not.\n");
   fprintf(stderr, " play              - Starts playing back the specified content at normal speed.\n");
   fprintf(stderr, " stop              - Stops playing back the content.\n");
   fprintf(stderr, " pause             - Stops playing back the content, and may resume to play it back\n");
   fprintf(stderr, "                     alternatively.\n");
   fprintf(stderr, " record            - Records the specified stream or content to the specified medium.\n");
   fprintf(stderr, " rewind            - Moves the position toward the beginning of the medium.\n");
   fprintf(stderr, " fast_forward      - Moves the position away from the beginning of the medium.\n");
   fprintf(stderr, " eject             - Ejects the medium from the device, and may close the door for\n");
   fprintf(stderr, "                     loading the medium alternatively.\n");
   fprintf(stderr, " forward           - Switches the contents, such as music tune, or video chapter,\n");
   fprintf(stderr, "                     which can be reproduced with .play. operation, to the forward\n");
   fprintf(stderr, "                     one. The 'forward' means future direction in time, plus\n");
   fprintf(stderr, "                     direction in number, and down direction in a list.\n");
   fprintf(stderr, " backward          - Switches the contents, such as music tune, or video chapter,\n");
   fprintf(stderr, "                     which can be reproduced with .play. operation, to the backward\n");
   fprintf(stderr, "                     one. The 'backward' means past direction in time, minus\n");
   fprintf(stderr, "                     direction in number, and up direction in a list.\n");
   fprintf(stderr, " angle             - Switches the scene of the contents, if it has multi angle contents.\n");
   fprintf(stderr, " subpicture        - Switches or rotates the sub pictures, if it has some sub pictures data.\n");
}

int main(int argc, char **argv) 
{
    raw1394handle_t handle;
    int 	node = -1, port = 0;
    int 	return_code;
    int 	c, i, j;
    int 	index;
    quadlet_t 	*avc_result;
    quadlet_t 	command[2];
    int 	cmd_length = 0;  /* Final command length to be sent */
    int		ext_length = 0;  /* Extra NULL parameters to be sent for some STBs */
    unsigned	key_press = AVC1394_PANEL_OPERAND_PRESS;  /* by default use a key press vs release */

    opterr = 0;
    while((c = getopt(argc, argv, "ehn:rtv")) != -1)
        {
        switch(c) 
            {
            case 'e':
                ext_length = 1;
                break;
            case 'h':
                usage_help();
		exit(-1);
                break;
            case 'n':
                node = atoi(optarg);
                if (node < 0)
                    {
                    fprintf(stderr, "invalid node: %d\n", node);
                    exit(-1);
                    }
                break;
            case 'r':
                key_press = AVC1394_PANEL_OPERAND_RELEASE;
                break;
            case 'v':
                verbose = 1;
                break;
            case 't':
                test_mode = 1;
                break;
            default:
                fprintf(stderr, "bad command line argument: %c\n", optopt);
                usage();
                exit(-1);
            }
    	}

    if (node == -1)
        {
        fprintf(stderr, "-n NODE option required\n");
        usage();
        exit(-1);
    	}

    index = optind;
    if (index == argc)
        {
        fprintf(stderr, "Command Missing\n");
        usage();
        exit(-1);
    	}
    else if (argc > (index+1))
        {
        fprintf(stderr, "too many options\n");
        usage();
        exit(-1);
    	}

    /* remove any characters used to aid readability  of command */
    for (i = j = 0; argv[index][i]; ++i)
	if ((argv[index][i] != '_') && (argv[index][i] != '+'))
	    argv[index][j++] = argv[index][i];
    argv[index][j] = 0;   /* ensure string is terminated */

    /* convert command to lower case */
    for (i=0; argv[index][i]; i++)
	argv[index][i] = tolower(argv[index][i]);

    if (!strcmp(argv[index], "poweron"))
        {
        command[0] = CMDSTB_ON;
	cmd_length = 1;
        } 
    else if (!strcmp(argv[index], "poweroff"))
        {
        command[0] = CMDSTB_OFF;
	cmd_length = 1;
        }
    else if (!strcmp(argv[index], "powerstatus"))
        {
        command[0] = CMDSTB_STATUS;
	cmd_length = 1;
        }
    else if (!strcmp(argv[index], "select"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_SELECT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "up"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "down"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "left"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_LEFT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "right"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_RIGHT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "rightup"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_RIGHT_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "rightdown"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_RIGHT_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "leftup"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_LEFT_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "leftdown"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_LEFT_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "rootmenu"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_ROOT_MENU | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "setupmenu"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_SETUP_MENU | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "contentsmenu"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_CONTENTS_MENU | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "favoritemenu"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_FAVORITE_MENU | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "exit"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_EXIT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "0"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_0 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "1"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_1 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "2"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_2 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "3"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_3 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "4"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_4 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "5"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_5 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "6"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_6 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "7"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_7 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "8"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_8 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "9"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_9 | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "dot"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_DOT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "enter"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_ENTER | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "clear"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_CLEAR | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "channelup"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_CHANNEL_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "channeldown"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_CHANNEL_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "previouschannel"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_PREVIOUS_CHANNEL | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "soundselect"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_SOUND_SELECT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "inputselect"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_INPUT_SELECT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "displayinfo"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_DISPLAY_INFO | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "help"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_HELP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "pageup"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_PAGE_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "pagedown"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_PAGE_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "powerbutton"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_POWER | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "volumeup"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_VOLUME_UP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "volumedown"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_VOLUME_DOWN | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "mute"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_MUTE | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "play"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_PLAY | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "stop"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_STOP | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "pause"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_PAUSE | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "record"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_RECORD | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "rewind"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_REWIND | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "fastforward"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_FASTFORWARD | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "eject"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_EJECT | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "forward"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_FORWARD | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "backward"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_BACKWARD | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "angle"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_ANGLE | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else if (!strcmp(argv[index], "subpicture"))
        {
        command[0] = CMDSTB_PANEL | AVC1394_PANEL_OPERATION_SUBPICTURE | key_press;
	command[1] = CMDSTB_PANEL_NO_PARAMETER;
	cmd_length = 1 + ext_length;
        }
    else
        {
        fprintf(stderr, "unknown command %s\n", argv[index]);
        usage();
        exit(-1);
        } 

    if (cmd_length==0)
        {
        fprintf(stderr, "Error in code logic. Command length is 0 but should not be\n");
	exit(-1);
	}
         

    if (!test_mode)
       handle = raw1394_new_handle_on_port(port);
    if (!handle && !test_mode)
        {
        fprintf(stderr, "Failed to get raw1394 handle, Port: %d\n", port);
        exit(-1);
        }

	/* Issue the command */
    avc_result = avc_cmd(handle, node, command, cmd_length);
    if ((avc_result[0] == CMDSTB_ERROR) && !test_mode)
        {
        fprintf(stderr, "Set top box command failed\n");
        raw1394_destroy_handle(handle);
        exit(-1);
        }

	/* After Issuing the command, if it is on, off, or status then return the actual   */
	/* state after execution of the command and translate to a return code.            */
	/* The set top box can take time to stabalize after the last command so wait a bit */
    if (((command[0] == (CMDSTB_ON)) ||
         (command[0] == (CMDSTB_OFF)) ||
         (command[0] == (CMDSTB_STATUS))) &&
        (!test_mode) )
        {
        if (command[0] != (CMDSTB_STATUS))
            {                           /* Would be redundant for status command */
            sleep(1);
  	    command[0] = CMDSTB_STATUS;
   	    avc_result = avc_cmd(handle, node, command, 1);
            }
        switch (avc_result[0] & CMDSTB_MASK)
            {
            case CMDSTB_OFF:
            	return_code = CMD_OFF;
                break;
            case CMDSTB_ON:
               	return_code = CMD_ON;
                break;
            case CMDSTB_ERROR:
               	fprintf(stderr, "Set top box command failed\n");
               	return_code = -1;
               	break;
            default:
                fprintf(stderr, "Unexpected return result: 0x%08X\n", avc_result[0]);
               	return_code = -1;
            }
        }
    else
        {
        return_code = CMD_SUCCESS;
        }

    if (verbose)
        printf("return code: %i\n", return_code);

    if (!test_mode)
        raw1394_destroy_handle(handle);
    exit(return_code);

}


quadlet_t *avc_cmd(raw1394handle_t handle, int node, quadlet_t cmd[], int cmd_length)
{
    quadlet_t *response;
    int  count = 0;

    if (verbose)
        {
        printf("  Sending Command:");
	while (count<cmd_length)
	    {
            printf("  0x%08X", cmd[count]);
	    count++;
	    }
        printf("\n");
    	}

    if (test_mode)
        {
        response[0] = CMDSTB_TEST;
        return(response);
        }

    response = avc1394_transaction_block(handle, node, cmd, cmd_length, 1);
    if (response)
        {
        if (AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_ACCEPTED ||
            AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_IMPLEMENTED)
            {
	    if (verbose)
                {
                printf("  Success, Response: 0x%08X\n", response[0]);
                }
            }
        else if (AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_NOT_IMPLEMENTED) 
	    {
            fprintf(stderr, "  Command Not Implemented on STB, Response: 0x%08X\n", response[0]);
            response[0] = CMDSTB_ERROR;
	    }
        else
            {
            fprintf(stderr, "  Command Failed, Response: 0x%08X\n", response[0]);
            response[0] = CMDSTB_ERROR;
            }
    	}
    else
        {
        fprintf(stderr, "  Command Failed, No Response\n");
        response[0] = CMDSTB_ERROR;
    	}

    return(response);
    
}

